﻿Imports FTTK.Services.Interfaces.Rest
Imports FTTK.Json
Imports System.Diagnostics.Contracts

Namespace FTTK.Services.Implementations.Rest
    Friend Class JsonParser
        Implements IJsonParser

        ''' <summary>
        ''' Parses the input string into array Jsonobjects.
        ''' </summary>
        ''' <param name="jsonString">The json string.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function ParseIntoArrayObjects(jsonString As String) As System.Collections.Generic.IEnumerable(Of Json.JsonObject) Implements Interfaces.Rest.IJsonParser.ParseIntoArrayObjects
            Dim retObj As IEnumerable(Of JsonObject) = Nothing
            jsonString = jsonString.Trim()
            retObj = ExtractArrayJsonObjects(jsonString)
            Return retObj
        End Function

        ''' <summary>
        ''' Parses the input string into single Json object.
        ''' </summary>
        ''' <param name="jsonString">The json string.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function ParseIntoSingleObject(jsonString As String) As Json.JsonObject Implements Interfaces.Rest.IJsonParser.ParseIntoSingleObject
            Dim retObj As JsonObject = Nothing
            jsonString = jsonString.Trim()
            retObj = ExtractSingleJsonObject(jsonString)
            Return retObj
        End Function

        Public Function Parse(str As String) As Object
            Dim retObj As Object = Nothing
            str = str.Trim()
            If str.StartsWith("{") Then
                retObj = ExtractSingleJsonObject(str)
            ElseIf str.StartsWith("[") Then
                retObj = ExtractArrayJsonObjects(str)
            End If
            Return retObj
        End Function

        Private Function ExtractInnerString(ByVal outerString As String, ByRef startIndex As Integer, ByRef endIndex As Integer) As String
            startIndex = -1
            endIndex = -1
            Dim innerString As String = Nothing
            Dim openBracket = 0
            Dim openSquareBracket = 0
            For index = 0 To outerString.Length - 1
                Select Case outerString.Chars(index)
                    Case "["c
                        openSquareBracket += 1
                    Case "]"c
                        openSquareBracket -= 1
                    Case "{"c
                        openBracket += 1
                        If openBracket = 1 Then startIndex = index
                    Case "}"c
                        openBracket -= 1
                        If openBracket = 0 And openSquareBracket = 0 Then
                            endIndex = index
                            Exit For
                        End If
                End Select
            Next
            If startIndex <> -1 And endIndex <> -1 And startIndex < endIndex Then
                innerString = outerString.Substring(startIndex + 1, endIndex - startIndex - 1)
            Else
                innerString = outerString
                startIndex = 0
                endIndex = outerString.Length - 1
            End If
            Return innerString
        End Function

        Private Function ExtractSingleJsonObject(str As String) As JsonObject
            Dim retObj As JsonObject = New JsonObject
            Dim startIndex = -1
            Dim endIndex = -1
            str = ExtractInnerString(str, startIndex, endIndex)
            Dim doublePointIndex = -1
            Dim openBracket = 0
            Dim numberOfQuote = 0
            Dim propertyName As String
            Dim propertyValue As String
            Dim extractPair As Boolean = False
            For index = 0 To str.Length - 1
                Select Case str.Chars(index)
                    Case ":"c
                        If openBracket = 0 And doublePointIndex = -1 Then doublePointIndex = index
                    Case "{"c, "["c
                        openBracket += 1
                    Case "}"c, "]"c
                        openBracket -= 1
                        extractPair = (index = str.Length - 1)
                    Case ","c
                        If numberOfQuote = 0 Then
                            extractPair = (openBracket = 0)
                        Else
                            extractPair = (index = str.Length - 1)
                        End If
                    Case CChar("""")
                        numberOfQuote = If(numberOfQuote = 0, 1, 0)
                        extractPair = (index = str.Length - 1)
                    Case Else
                        extractPair = (index = str.Length - 1)
                End Select
                If extractPair Then
                    If openBracket = 0 AndAlso doublePointIndex <> -1 AndAlso doublePointIndex < index Then
                        propertyName = str.Substring(startIndex, doublePointIndex - startIndex)
                        propertyName = propertyName.Replace("""", "").Trim()
                        If index = str.Length - 1 Then
                            propertyValue = str.Substring(doublePointIndex + 1, index - doublePointIndex)
                        Else
                            propertyValue = str.Substring(doublePointIndex + 1, index - doublePointIndex - 1)
                        End If
                        startIndex = index + 1
                        doublePointIndex = -1
                        If propertyValue.StartsWith("{") Then
                            retObj.Add(propertyName, ExtractSingleJsonObject(propertyValue))
                        ElseIf propertyValue.StartsWith("[") Then
                            retObj.Add(propertyName, ExtractArrayJsonObjects(propertyValue))
                        ElseIf propertyValue.StartsWith("""") Then
                            retObj.Add(propertyName, propertyValue.Replace("""", ""))
                        Else
                            retObj.Add(propertyName, propertyValue)
                        End If
                        extractPair = False
                    End If
                End If

            Next
            Return retObj
        End Function

        Private Function ExtractArrayJsonObjects(propertyValue As String) As List(Of JsonObject)
            Dim retList = New List(Of JsonObject)
            If propertyValue.StartsWith("[") Then propertyValue = propertyValue.Substring(1)
            If propertyValue.EndsWith("]") Then propertyValue = propertyValue.Substring(0, propertyValue.Length - 1)
            If Not String.IsNullOrEmpty(propertyValue) Then
                Dim startIndex As Integer
                Dim endIndex As Integer
                Do
                    Dim innerString = ExtractInnerString(propertyValue, startIndex, endIndex)
                    If Not String.IsNullOrEmpty(innerString) Then
                        retList.Add(ExtractSingleJsonObject(innerString))
                    End If
                    If endIndex < propertyValue.Length - 1 Then
                        propertyValue = propertyValue.Substring(endIndex + 1)
                    End If
                Loop While endIndex < propertyValue.Length - 1

            End If
            Return retList
        End Function
    End Class
End Namespace